package pl.rzarajczyk.breaktime.utils;

import com.google.common.base.Throwables;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import java.awt.Component;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileSystemView;
import org.apache.commons.logging.Log;
import pl.rzarajczyk.utils.os.OperatingSystem;

/**
 *
 * @author rzarajczyk
 */
public class Utils {

    public static int SECOND = 1000;
    
    private static FileSystemView fsv = FileSystemView.getFileSystemView();
    
    // -------------------------------------------------------------------------
    
    /**
     * For JUnit tests
     * @param fileSystemView 
     */
    static void setFileSystemView(FileSystemView fileSystemView) {
        fsv = fileSystemView;
    }
    // -------------------------------------------------------------------------

    public static String toTimeString(int ms) {
        boolean negative = ms < 0;
        if ( negative ) {
            ms = -ms;
        }
        int sec = ms / Utils.SECOND;
        int min = sec / 60;
        sec = sec % 60;
        StringBuilder result = new StringBuilder();
        if ( negative ) {
            result.append("-");
        }
        result.append(min < 10 ? "0" + min : min);
        result.append(":");
        result.append(sec < 10 ? "0" + sec : sec);
        return result.toString();
    }

    public static int parseTimeString(String str) {
        int multiplier = 1;
        if ( str.trim().startsWith("-") ) {
            multiplier = -1;
        }
        String [] parts = str.split(":");
        if ( parts.length != 2 ) {
            throw new IllegalArgumentException("Unable to parse string: " + str);
        }
        int minutes = Math.abs( Integer.parseInt(parts[0]) );
        int seconds = Integer.parseInt(parts[1]);
        if ( seconds < 0 )
            throw new IllegalArgumentException("Unable to parse string: " + str);
        return ( minutes * 60 + seconds ) * Utils.SECOND * multiplier;
    }

    public static File getPrivateDir() throws IOException {
        File dir = fsv.getHomeDirectory();
        if ( OperatingSystem.isWindows() ) {
            dir = new File( System.getenv("USERPROFILE") );
        }
        dir = new File(dir, ".breaktime");
        if ( ! dir.exists() ) {
            if ( ! dir.mkdirs() )
                throw new IOException("Could not create directory structure: " + dir.getAbsolutePath());
        }
        return dir;
    }

    public static File getTempDir() throws IOException {
        File file = File.createTempFile("breaktime-temp-file", ".tmp");
        File dir = file.getParentFile();
        file.delete();
        
        return dir;
    }

    public static void error(Component parent, Log log, String message, Throwable e) {
        log.error(message, e);
        String rootMessage = Throwables.getRootCause(e).getMessage();
        JOptionPane.showMessageDialog(parent, message + ":\n" + rootMessage, "Error occured", JOptionPane.ERROR_MESSAGE);
        System.exit(1);
    }
    
    static URI getSourceCodeUri() throws IOException {
        Class<?> clazz = Utils.class;
        try {
            return clazz.getProtectionDomain().getCodeSource().getLocation().toURI();
        } catch (URISyntaxException e) {
            throw new IOException(e);
        }        
    }
    
    static boolean isPackedApplication() throws IOException {
        URI uri = getSourceCodeUri();
        File file = new File(uri);
        return file.isFile();
    }

    public static void unpackFromClasspathDir(String classpathResource, File dir) throws IOException {
        if ( isPackedApplication() ) {
            unpackFromJarClasspathDir(classpathResource, dir);
        } else {
            unpackFromDirClasspathDir(classpathResource, dir);
        }
    }
    
    public static void unpackFromJarClasspathDir(String classpathResource, File dir) throws IOException {
        JarFile jarFile = new JarFile(getSourceCodeUri().getPath());
        try {
            Enumeration<JarEntry> enumeration = jarFile.entries();
            while (enumeration.hasMoreElements()) {
                JarEntry entry = enumeration.nextElement();
                String name = entry.getName();
                if ( name.startsWith(classpathResource) ) {
                    if ( name.endsWith("/") ) {
                        File subdir = new File(dir, name);
                        if ( ! subdir.mkdirs() )
                            throw new IOException("Could not create dir: " + subdir);
                    } else {
                        name = "/" + name;
                        File file = new File(dir, name);
                        InputStream input = Utils.class.getResourceAsStream(name);
                        OutputStream output = new BufferedOutputStream(new FileOutputStream(file));
                        try {
                            ByteStreams.copy(input, output);
                        } catch (IOException e) {
                            throw new IOException("Error during copying from classpath:" + name + " to file:" + file, e);
                        } finally {
                            output.close();
                        }
                    }
                }
            }
        } finally {
            jarFile.close();
        }
    }
    
    static List<String> getDirContents(File dir, String prefix) {
        List<String> result = new ArrayList<String>();
        for ( File file : dir.listFiles() ) {
            if ( file.isFile() ) {
                result.add(prefix + file.getName());
            } else {
                result.add( prefix + file.getName()+ File.separator );
                result.addAll( getDirContents(file, prefix + file.getName() + File.separator) );
            }
        }
        return result;
    }
    
    public static void unpackFromDirClasspathDir(String classpathResource, File dir) throws IOException {
        File sourceDir = new File( getSourceCodeUri() );
        List<String> contents = getDirContents(sourceDir, "");
        for ( String name : contents ) {
            if ( name.startsWith(classpathResource) ) {
                if ( name.endsWith( File.separator ) ) {
                    File subdir = new File(dir, name);
                    if ( ! subdir.mkdirs() )
                        throw new IOException("Could not create dir: " + subdir);
                } else {
                    File fileFrom = new File(sourceDir, name);
                    File toFile = new File(dir, name);
                    Files.copy(fileFrom, toFile);
                }
            }
        }
    }
}